package com.bigdata.web.controller.system;


import cn.dev33.satoken.annotation.SaCheckPermission;
import cn.dev33.satoken.annotation.SaIgnore;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.aliyun.oss.OSSClientBuilder;
import com.aliyun.oss.common.utils.BinaryUtil;
import com.aliyun.oss.model.MatchMode;
import com.aliyun.oss.model.PolicyConditions;
import com.bigdata.common.annotation.Log;
import com.bigdata.common.core.controller.BaseController;
import com.bigdata.common.core.domain.PageQuery;
import com.bigdata.common.core.domain.R;
import com.bigdata.common.core.page.TableDataInfo;
import com.bigdata.common.core.validate.QueryGroup;
import com.bigdata.common.enums.BusinessType;
import com.bigdata.common.utils.DateUtils;
import com.bigdata.oss.core.OssClient;
import com.bigdata.oss.entity.OssPolicyBo;
import com.bigdata.oss.enumd.OssKeyEnum;
import com.bigdata.oss.factory.OssFactory;
import com.bigdata.oss.properties.OssProperties;
import com.bigdata.system.domain.bo.SysOssBo;
import com.bigdata.system.domain.vo.SysOssVo;
import com.bigdata.system.service.ISysOssService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.MediaType;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletResponse;
import javax.validation.constraints.NotEmpty;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.*;
import com.aliyun.oss.OSS;

/**
 * 文件上传 控制层
 *
 * @author Lion Li
 */
@Validated
@RequiredArgsConstructor
@RestController
@RequestMapping("/system/oss")
@Slf4j
public class SysOssController extends BaseController {

    private final ISysOssService iSysOssService;

    /**
     * 查询OSS对象存储列表
     */
    @SaIgnore
    @GetMapping("/list")
    public TableDataInfo<SysOssVo> list(@Validated(QueryGroup.class) SysOssBo bo, PageQuery pageQuery) {
        return iSysOssService.queryPageList(bo, pageQuery);
    }

    /**
     * 查询OSS对象基于id串
     *
     * @param ossIds OSS对象ID串
     */
    @SaIgnore
    @GetMapping("/listByIds/{ossIds}")
    public R<List<SysOssVo>> listByIds(@NotEmpty(message = "主键不能为空")
                                       @PathVariable Long[] ossIds) {
        List<SysOssVo> list = iSysOssService.listByIds(Arrays.asList(ossIds));
        return R.ok(list);
    }

    /**
     * 上传OSS对象存储
     *
     * @param file 文件
     */
    @SaIgnore
    @Log(title = "OSS对象存储", businessType = BusinessType.INSERT)
    @PostMapping(value = "/upload", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
    public R<Map<String, String>> upload(@RequestPart("file") MultipartFile file) {
        if (ObjectUtil.isNull(file)) {
            return R.fail("上传文件不能为空");
        }
        SysOssVo oss = iSysOssService.upload(file);
        Map<String, String> map = new HashMap<>(2);
        map.put("url", oss.getUrl());
        map.put("fileName", oss.getOriginalName());
        map.put("ossId", oss.getOssId().toString());
        return R.ok(map);
    }

    /**
     * 下载OSS对象
     *
     * @param ossId OSS对象ID
     */
    @SaCheckPermission("system:oss:download")
    @GetMapping("/download/{ossId}")
    public void download(@PathVariable Long ossId, HttpServletResponse response) throws IOException {
        iSysOssService.download(ossId,response);
    }

    /**
     * 删除OSS对象存储
     *
     * @param ossIds OSS对象ID串
     */
    @SaCheckPermission("system:oss:remove")
    @Log(title = "OSS对象存储", businessType = BusinessType.DELETE)
    @DeleteMapping("/{ossIds}")
    public R<Void> remove(@NotEmpty(message = "主键不能为空")
                          @PathVariable Long[] ossIds) {
        return toAjax(iSysOssService.deleteWithValidByIds(Arrays.asList(ossIds), true));
    }


    /**
     * 前端获取授权
     *
     * @param ossPolicy
     * @return
     * @see <a ref="https://help.aliyun.com/zh/oss/use-cases/obtain-signature-information-from-the-server-and-upload-data-to-oss?spm=a2c4g.11186623.4.5.2d944f23vUAEv7&scm=20140722.H_31926._.ID_31926-OR_rec-V_1">官方文档</a>
     */
    @SaIgnore
    @PostMapping("/policy")
    public R policy(@RequestBody OssPolicyBo ossPolicy) {
        Map<String, String> result = null;
        try {
            OssKeyEnum ossKeyEnum = OssKeyEnum.getByKey(ossPolicy.getOssKey());
            OssClient instance = OssFactory.instance(ossKeyEnum.getKey());
            OssProperties properties = instance.getProperties();
            OSS ossClient = new OSSClientBuilder().build(properties.getEndpoint(), properties.getAccessKey(), properties.getSecretKey());
            String host = String.format("https://%s.%s", properties.getBucketName(), properties.getEndpoint());
            String dir = String.format("%s/%s", DateUtils.datePath(), IdUtil.fastSimpleUUID());
            if (StrUtil.isNotBlank(properties.getPrefix())) {
                dir = String.format("%s/%s", properties.getPrefix(), dir);
            }
            long expireTime = 30;
            long expireEndTime = System.currentTimeMillis() + expireTime * 1000;
            Date expiration = new Date(expireEndTime);
            PolicyConditions policyConditions = new PolicyConditions();
            policyConditions.addConditionItem(PolicyConditions.COND_CONTENT_LENGTH_RANGE, 0, 1048576000);
            policyConditions.addConditionItem(MatchMode.StartWith, PolicyConditions.COND_KEY, dir);
            String postPolicy = ossClient.generatePostPolicy(expiration, policyConditions);
            byte[] binaryData = postPolicy.getBytes(StandardCharsets.UTF_8);
            String encodedPolicy = BinaryUtil.toBase64String(binaryData);
            String signature = ossClient.calculatePostSignature(postPolicy);

            result = new LinkedHashMap<String, String>();
            result.put("accessId", properties.getAccessKey());
            result.put("policy", encodedPolicy);
            result.put("signature", signature);
            result.put("dir", dir);
            result.put("host", host);
            result.put("expire", String.valueOf(expireEndTime / 1000));
        } catch (Exception e) {
            log.error("get aliyun oss policy error", e);
        }
        return R.ok(result);
    }
}
